<script setup lang="ts">
import { ref, type Ref, nextTick } from 'vue'
import { YiTable, YiAction, YiModal } from '@uublue/yimi-element-plus'
import { ElSelect, ElTabs, ElMessageBox, ElMessage } from 'element-plus'
import { FolderOpened, MagicStick, Document } from '@element-plus/icons-vue'
import RoleForm from './role-form.vue'
import { mapRecursively } from '@/utils/array-recursion'
import SecurityDisplay from '../api/security-display.vue'
import RolePermissionAction from './role-permission-action.vue'
import { formatMenuItemApiTree, type Resource } from '@/utils/resource-security'
import axiosInst from '@/utils/request'
import pinia from '@/stores/index'
import { usePage } from '@/stores/page'

const pageStore = usePage(pinia)
const rolePermissionTable: Ref<typeof YiAction | null> = ref(null)
const menuItemGroupOptions: Ref<Record<string, any>[]> = ref([])
const menuItemGroup: Ref<string> = ref('local')
const roleCodeNameMap: Ref<Record<string, string>> = ref({})
const menuItemApiTreeMap: Record<string, Resource[]> = {}
const roleTableRefreshTimes = ref(0)
const addPermissionParts: Ref<string[]> = ref([])
const addPermissionOptions: Ref<string[][]> = ref([])

function loadMenuItemApiTree(forceFetch: boolean = false): Promise<Resource[]> {
  if (forceFetch || !menuItemApiTreeMap[menuItemGroup.value]) {
    return new Promise<Resource[]>((resolve) => {
      axiosInst.request(getMenuApiTree(menuItemGroup.value)).then((res) => {
        menuItemApiTreeMap[menuItemGroup.value] = formatMenuItemApiTree(res.data.rows as Resource[])
        resolve(menuItemApiTreeMap[menuItemGroup.value])
      })
    })
  } else {
    return new Promise<Resource[]>((resolve) => {
      resolve(menuItemApiTreeMap[menuItemGroup.value])
    })
  }
}

function loadRoleSecurity(role: Record<string, any>): Promise<Record<string, any>> {
  return new Promise<Record<string, any>>((resolve) => {
    axiosInst.request(listRolePermission(role.id)).then((res) => {
      const ownedPermissions: string[] = res.data
        .filter((rp: any) => rp.permissionCode != undefined)
        .map((rp: any) => {
          return rp.permissionCode
        })

      const allPermissions = ownedPermissions
        ? ownedPermissions.concat(role.defaultPermissions || [])
        : role.defaultPermissions

      resolve({
        id: role.id,
        code: role.code,
        allPermissions,
        defaultPermissions: role.defaultPermissions || [],
        ownedPermissions: ownedPermissions || []
      })
    })
  })
}

function loadMenuGroups() {
  axiosInst.request(getMenuGroups).then((res) => {
    // menuItemGroupOptions.value = new OptionsMaker('code', 'label').make(res.data)
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    menuItemGroupOptions.value = mapRecursively(res.data as any[], (menuGroup) => {
      return {
        value: menuGroup.code,
        label: menuGroup.label,
        children: menuGroup.children
      }
    })
  })
}

//on create
loadMenuGroups()
</script>

<!-- 定义api -->
<script lang="ts">
import roleApis from '@/apis/system/role'
import menuApis from '@/apis/system/menu'
import permissionApis from '@/apis/system/permission'
import type { AxiosRequestConfig } from 'axios'

const {
  queryRoles,
  addRole,
  updateRole,
  removeRole,
  listRolePermission,
  associateRolePermission,
  disassociateRolePermission,
  setRolePermissions
} = roleApis
const { getMenuApiTree, getMenuGroups } = menuApis
const { getPermissionPart } = permissionApis

export default {
  name: 'RoleView',
  apis: {
    queryRoles,
    addRole,
    updateRole,
    removeRole,
    listRolePermission,
    getMenuApiTree,
    associateRolePermission,
    disassociateRolePermission,
    setRolePermissions,
    getMenuGroups,
    getPermissionPart
  },
}
</script>

<template>
  <div class="app-container">
    <yi-table
      :api="queryRoles"
      :tools="['refresh']"
      :columns="[
        {
          label: '角色名称',
          prop: 'name'
        },
        {
          label: '角色编码',
          prop: 'code'
        },
        {
          label: '状态',
          prop: 'status',
          slot: 'status'
        },
        {
          label: '来源',
          prop: 'undeletable',
          slot: 'undeletable'
        },
        {
          label: '操作',
          prop: 'opt',
          slot: 'opt',
          width: '360px'
        }
      ]"
      :res-adapter="
        (res) => {
          ;(res.data.rows as any[]).forEach((row) => {
            roleCodeNameMap[row.code] = row.name
          })
          return {
            rows: res.data.rows,
            total: res.data.total
          }
        }
      "
      default-expand-all
    >
      <template #conditions="{ model, refresh }">
        <el-form inline :model="model" size="small">
          <el-form-item label="角色名称">
            <el-input v-model="model.name" placeholder="请输入角色名称"></el-input>
          </el-form-item>
          <el-form-item label="角色代码">
            <el-input v-model="model.code" placeholder="请输入角色代码"></el-input>
          </el-form-item>

          <el-form-item>
            <el-button type="primary" @click="refresh()">查询</el-button>
          </el-form-item>
        </el-form>
      </template>

      <template #actions="{ refresh }">
        <el-row>
          <!-- 添加 -->
          <yi-action
            :api="addRole"
            text="添加"
            type="primary"
            size="small"
            :plain="true"
            icon="el-icon-plus"
            modal-title="添加角色"
            dialog-width="500px"
            @on-submit-success="refresh"
          >
            <template #default="scope">
              <role-form v-model="scope.model" :form-ref="scope.form" use-for="add" />
            </template>
          </yi-action>
        </el-row>
      </template>

      <template #status="{ row }">
        <el-tag v-if="row.status" type="success">可用</el-tag>
        <el-tag v-else type="danger">不可用</el-tag>
      </template>
      <template #undeletable="{ row }">
        <el-tag v-if="row.undeletable" type="info">系统内置</el-tag>
        <el-tag v-else type="primary">用户创建</el-tag>
      </template>
      <template #opt="{ row, refresh }">
        <!-- 按钮：修改角色 -->
        <yi-action
          :api="updateRole"
          type="primary"
          trigger="link"
          :underline="false"
          text="修改"
          size="default"
          icon="el-icon-edit"
          :model="row"
          modal-title="修改角色"
          dialog-width="500px"
          @on-submit-success="
            (res, model) => {
              Object.assign(row, model)
            }
          "
        >
          <template #default="scope">
            <role-form v-model="scope.model" :form-ref="scope.form" use-for="edit" />
          </template>
        </yi-action>

        <!-- 按钮：权限 -->
        <YiModal
          text="授权"
          icon="el-icon-key"
          type="primary"
          trigger="link"
          :on-open="
            ({ resolve, context }) => {
              roleTableRefreshTimes = 0
              loadMenuItemApiTree().then((resourceData) => {
                loadRoleSecurity(row).then((role) => {
                  resolve({
                    currentTab: context.currentTab || 'resource',
                    menuItemApiTree: resourceData,
                    role
                  })
                })
              })
            }
          "
          :underline="false"
          dialog-width="70%"
          size="default"
          :modal-title="'【' + row.name + '】'"
        >
          <template #default="{ context, setContext }">
            <el-tabs v-model="context.currentTab" type="border-card">
              <el-tab-pane label="资源" name="resource" lazy>
                <yi-table
                  ref="rolePermissionTable"
                  class="menu-table"
                  :columns="[
                    {
                      label: '资源名',
                      prop: 'title',
                      showOverflowTooltip: true,
                      formatter: (row, column, cellValue) => row.title || row.name
                    },
                    {
                      label: '类型',
                      prop: 'type',
                      slot: 'type'
                    },
                    {
                      label: '所需角色',
                      prop: 'roleRequirement',
                      slot: 'roles'
                    },
                    {
                      label: '所需权限',
                      prop: 'permissionRequirement',
                      showOverflowTooltip: true,
                      slot: 'permissions'
                    },
                    {
                      label: '是否拥有',
                      prop: 'matched',
                      slot: 'matched'
                    }
                  ]"
                  max-height="620px"
                  row-key="key"
                  :data="context.menuItemApiTree"
                  :tools="[]"
                  :paged="false"
                  empty-text="尚无资源，请先完成菜单和接口的同步操作！"
                  @redraw-complete="
                    (rows) => {
                      if (roleTableRefreshTimes == 0) {
                        rows?.forEach((_row) => {
                          if (rolePermissionTable) {
                            rolePermissionTable?.toggleRowExpansion(_row, true)
                          }
                        })
                      }
                      roleTableRefreshTimes++
                    }
                  "
                >
                  <template #actions>
                    <el-select
                      v-model="menuItemGroup"
                      placeholder="选择菜单组"
                      size="small"
                      style="width: 240px"
                      @change="
                        () => {
                          roleTableRefreshTimes = 0
                          loadMenuItemApiTree().then((resourceData) => {
                            setContext({
                              menuItemApiTree: resourceData
                            })
                          })
                        }
                      "
                    >
                      <el-option
                        v-for="(item, index) in menuItemGroupOptions"
                        :key="index"
                        :label="item.label"
                        :value="item.value"
                      />
                    </el-select>
                  </template>
                  <template #tools>
                    <el-tooltip class="item" effect="dark" content="刷新" placement="top">
                      <el-button
                        circle
                        size="small"
                        icon="el-icon-refresh"
                        @click="
                          () => {
                            roleTableRefreshTimes = 0
                            loadMenuItemApiTree(true).then((resourceData) => {
                              loadRoleSecurity(row).then((role) => {
                                setContext({
                                  menuItemApiTree: resourceData,
                                  role
                                })
                              })
                            })
                          }
                        "
                      >
                      </el-button>
                    </el-tooltip>
                  </template>
                  <template #type="{ value, row: subRow }">
                    <el-link
                      v-if="value == 'Api'"
                      type="warning"
                      :underline="false"
                      :icon="MagicStick"
                      >操作</el-link
                    >
                    <el-link
                      v-else-if="value == 'Page'"
                      type="primary"
                      :underline="false"
                      :icon="Document"
                      @click="
                        () => {
                          rolePermissionTable?.toggleRowExpansion(subRow)
                        }
                      "
                      >页面</el-link
                    >
                    <el-link
                      v-else
                      :underline="false"
                      :icon="FolderOpened"
                      @click="
                        () => {
                          rolePermissionTable?.toggleRowExpansion(subRow)
                        }
                      "
                      >目录</el-link
                    >
                  </template>
                  <template #roles="{ value }">
                    <template v-if="value">
                      <security-display
                        :codes="value"
                        :owned-codes="[row.code]"
                        :code-name-map="roleCodeNameMap"
                      />
                    </template>
                  </template>
                  <template #permissions="{ value }">
                    <template v-if="value">
                      <security-display :codes="value" :owned-codes="context.role.allPermissions" />
                    </template>
                  </template>

                  <template #matched="{ row: subRow }">
                    <template v-if="subRow.type != 'Directory'">
                      <RolePermissionAction
                        :api="setRolePermissions"
                        :current-role="context.role"
                        :current-resource="subRow"
                        @on-submit-success="
                          () => {
                            loadRoleSecurity(row).then((role) => {
                              nextTick(() => {
                                setContext({
                                  role
                                })
                              })
                            })
                          }
                        "
                      />
                    </template>
                  </template>
                </yi-table>
              </el-tab-pane>
              <el-tab-pane label="权限" name="permission" lazy>
                <el-row :gutter="20">
                  <el-col :span="14" :xs="0">
                    <YiTable
                      :columns="[
                        {
                          label: '关联权限',
                          prop: 'permission'
                        },
                        {
                          label: '操作',
                          prop: 'opt',
                          width: 150,
                          slot: 'opt'
                        }
                      ]"
                      :data="
                        context.role.ownedPermissions.map((code: string) => {
                          return {
                            permission: code
                          }
                        })
                      "
                      :tools="[]"
                    >
                      <template #actions>
                        <YiAction
                          :api="associateRolePermission"
                          icon="el-icon-plus"
                          text="添加"
                          type="primary"
                          size="small"
                          :plain="true"
                          :modal-title="'为【' + row.name + '】添加权限'"
                          model-reserve="never"
                          :on-open="
                            ({ resolve }) => {
                              if (!addPermissionOptions[0]) {
                                axiosInst
                                  .request(getPermissionPart() as AxiosRequestConfig)
                                  .then((res) => {
                                    const permissionCodes = res.data
                                    addPermissionOptions[0] = ['*'].concat(permissionCodes)
                                    resolve()
                                  })
                              } else {
                                resolve()
                              }
                            }
                          "
                          :on-submit="
                            (scope, request, close) => {
                              if(addPermissionParts.length < addPermissionOptions.length) {
                                ElMessage({ message: '权限片段不能为空', type: 'error', duration: 3 * 1000 })
                                return
                              }
                              request({
                                params: {
                                  roleId: row.id,
                                  permissionCode: addPermissionParts.join(':')
                                }
                              }).then((res) => {
                                close(true)
                                loadRoleSecurity(row).then((role) => {
                                  nextTick(() => {
                                    setContext({
                                      role
                                    })
                                  })
                                })
                              })
                            }
                          "
                        >
                          <template #default>
                            <el-form>
                              <el-select
                                v-for="(permissionCodes, index) in addPermissionOptions"
                                :key="index"
                                v-model="addPermissionParts[index]"
                                filterable
                                size="small"
                                placeholder="权限片段"
                                style="width: 200px"
                                clearable
                                @change="
                                  (selectPart) => {
                                    const _length = addPermissionParts.length
                                    const __length = addPermissionOptions.length
                                    for (var i = index + 1; i < _length; i++) {
                                      addPermissionParts.pop()
                                    }
                                    for (var i = index + 1; i < __length; i++) {
                                      addPermissionOptions.pop()
                                    }
                                    if (selectPart) {
                                      const ahead = addPermissionParts.join(':')
                                      axiosInst
                                        .request(getPermissionPart(ahead) as AxiosRequestConfig)
                                        .then((res) => {
                                          const permissionCodes = res.data
                                          if (
                                            Array.isArray(permissionCodes) &&
                                            permissionCodes.length > 0
                                          ) {
                                            addPermissionOptions[index + 1] = ['*'].concat(
                                              permissionCodes
                                            )
                                          }
                                        })
                                    }
                                  }
                                "
                              >
                                <el-option
                                  v-for="(code, _index) in permissionCodes"
                                  :key="_index"
                                  :label="code"
                                  :value="code"
                                />
                              </el-select>
                            </el-form>
                          </template>
                        </YiAction>
                      </template>

                      <template #opt="{ row: subPermissionRow }">
                        <yi-action
                          v-model:loading="pageStore.loading"
                          title="删除"
                          :api="disassociateRolePermission(row.id, subPermissionRow.permission)"
                          type="primary"
                          trigger="link"
                          :underline="false"
                          text="删除"
                          size="default"
                          icon="el-icon-delete"
                          :confirm="
                            (resolve, reject) => {
                              ElMessageBox.confirm(
                                '是否删除该角色下的权限【' + subPermissionRow.permission + '】？',
                                '提示',
                                {
                                  confirmButtonText: '确定',
                                  cancelButtonText: '取消',
                                  type: 'warning',
                                  appendTo: 'body'
                                }
                              )
                                .then(() => {
                                  resolve()
                                })
                                .catch(() => {
                                  reject()
                                })
                            }
                          "
                          @on-submit-success="
                            () => {
                              loadRoleSecurity(row).then((role) => {
                                nextTick(() => {
                                  setContext({
                                    role
                                  })
                                })
                              })
                            }
                          "
                        />
                      </template>
                    </YiTable>
                  </el-col>
                  <el-col :span="10" :xs="0">
                    <YiTable
                      :columns="[
                        {
                          label: '固有权限',
                          prop: 'permission'
                        }
                      ]"
                      :data="
                        context.role.defaultPermissions.map((code: string) => {
                          return {
                            permission: code
                          }
                        })
                      "
                      :tools="[]"
                    >
                      <template
                        #pagination="{
                          currentPage,
                          pageSize,
                          total,
                          onSizeChange,
                          onCurrentChange
                        }"
                      >
                        <el-pagination
                          background
                          :current-page="currentPage"
                          :page-size="pageSize"
                          layout="total, sizes, prev, pager, next"
                          :pager-count="5"
                          :total="total"
                          @size-change="onSizeChange"
                          @current-change="onCurrentChange"
                        />
                      </template>
                    </YiTable>
                  </el-col>
                </el-row>
              </el-tab-pane>
            </el-tabs>
          </template>
        </YiModal>

        <!-- 按钮：删除角色 -->
        <yi-action
          title="删除角色"
          :api="removeRole(row.id)"
          type="primary"
          trigger="link"
          :underline="false"
          text="删除"
          size="default"
          icon="el-icon-delete"
          :disabled="row.undeletable"
          modal-title="删除角色"
          confirm-text="是否删除该角色？"
          @on-submit-success="
            () => {
              refresh()
            }
          "
        />
      </template>
    </yi-table>
  </div>
</template>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
.menu-table .el-table__expand-icon {
  width: 20px !important;
}
</style>
